//
//  tdl/net/btypes.h
//  Created by tommy on 13/04/25.
//

#ifndef tdl_net_btypes_h
#define tdl_net_btypes_h

extern "C" {
#include <time.h>
#include <arpa/inet.h>
}	// extern "C"
#include <string>
#include <ostream>
using namespace std;
#include "utils/string.h"
using namespace tdl;

namespace tdl {
///////////////////
struct ip_end
{
	uint32_t	m_ipv4;		// store in network-byte-order
public:
	ip_end() : m_ipv4(INADDR_ANY) {}
	ip_end(in_addr_t ina) : m_ipv4(ina) {}
	ip_end(const char* ip) { m_ipv4 = htonl(tdl::s2ipv4(std::string(ip))); }
	ip_end(const std::string &ip) { m_ipv4 = htonl(tdl::s2ipv4(ip)); }
	
public:
	ip_end& operator=(const ip_end& e) { m_ipv4 = e.m_ipv4; return *this; }
	ip_end& operator=(const std::string& ip) { m_ipv4 = htonl(tdl::s2ipv4(ip)); return *this; }

	bool operator==(const ip_end& e) const { return m_ipv4 == e.m_ipv4; }
	bool operator!=(const ip_end& e) const { return m_ipv4 != e.m_ipv4; }
	bool operator< (const ip_end& e) const { return m_ipv4 < e.m_ipv4; }
	
	void reset() { m_ipv4 = 0; }
	uint32_t to_int() const { return m_ipv4; }
	std::string to_string() const { return ipv42s(ntohl(m_ipv4)); }
	
	friend inline std::ostream& operator<<(std::ostream &ost, const ip_end& e)
	{
		ost << e.to_string();
		return ost;
	}
};

////////////////
struct host_addr
{
protected:
	sockaddr_in		m_addr;

protected:
	void _make_addr(ip_end ip, uint16_t port)
	{
		::memset(&m_addr, 0, sizeof(sockaddr_in));

		m_addr.sin_family = PF_INET;
		m_addr.sin_port = htons(port);
		m_addr.sin_addr.s_addr = ip.to_int();
	}

	void _s2addr(const std::string& s)
	{
		std::vector<std::string> vs;
		std::string tmps(s.data());
		tdl::split_string(tmps, ":", vs);

		ip_end ip = (vs[0] == "*") ? std::string("0.0.0.0") : vs[0];
		uint16_t port = tdl::s2u(vs[1]);
		_make_addr(ip, port);
	}

public:
	host_addr() { ::memset(&m_addr, 0, sizeof(sockaddr_in)); }
	
	host_addr(const char* s) { _s2addr(s); }
	host_addr(const std::string& s) { _s2addr(s); }
	
	host_addr(const char* ip, uint16_t port) { _make_addr(ip_end(ip), port); }
	host_addr(const std::string& ip, uint16_t port) { _make_addr(ip_end(ip), port); }
	host_addr(const ip_end& ip, uint16_t port) { _make_addr(ip, port); }
	
	host_addr(const host_addr& ha) { ::memcpy(&m_addr, &ha.m_addr, sizeof(sockaddr_in)); }
	host_addr(const struct sockaddr& sa) { ::memcpy(&m_addr, &sa, sizeof(struct sockaddr)); }
	host_addr(const struct sockaddr_in& sa_in) { ::memcpy(&m_addr, &sa_in, sizeof(struct sockaddr_in)); }

	host_addr& operator=(const std::string& s) { _s2addr(s); return *this; }
	host_addr& operator=(const host_addr& ha) { ::memcpy(&m_addr, &ha.m_addr, sizeof(sockaddr_in)); return *this; }
	host_addr& operator=(const struct sockaddr& sa) { ::memcpy(&m_addr, &sa, sizeof(struct sockaddr)); return *this; }
	host_addr& operator=(const struct sockaddr_in& sa_in) { ::memcpy(&m_addr, &sa_in, sizeof(struct sockaddr_in)); return *this; }
	
	bool operator==(const host_addr& ha) const { return (m_addr.sin_addr.s_addr == ha.m_addr.sin_addr.s_addr && m_addr.sin_port == ha.m_addr.sin_port); }
	bool operator==(const std::string& s) const { host_addr ha(s); return ha == *this; }
	bool operator!=(const host_addr& ha) const { return (m_addr.sin_addr.s_addr != ha.m_addr.sin_addr.s_addr || m_addr.sin_port != ha.m_addr.sin_port); }
	bool operator!=(const std::string& s) const { host_addr ha(s); return ha != *this; }
	
	sockaddr_in* get_inaddr() { return &m_addr; }

	sockaddr* get_addr() const { return (sockaddr*)&m_addr; }

	ip_end get_ip() const { return ip_end(m_addr.sin_addr.s_addr); }

	uint16_t get_port() const { return ntohs(m_addr.sin_port); }
	
	friend inline std::ostream& operator<<(std::ostream& ost, const host_addr& ha)
	{
		ost << ha.get_ip().to_string() << ":" << ha.get_port();
		return ost;
	}
};

////////////////
}

#endif

